//
// @Author: zhengqi Wu
// @Description: 名称组件
// @Version: 1.0.0
// @Date: 2021/3/12 11:08
// @Copyright: MIN-Group；国家重大科技基础设施——未来网络北大实验室；深圳市信息论与未来网络重点实验室
//

// @Description:
//	1. 参考 => https://named-data.net/doc/tech-memos/naming-conventions.pdf ，借鉴但不是按这个实现的
//	2. 格式如下：
//		IdentifierComponent = 100 TLV-LENGTH
//                      <Marker>
//                      *OCTET
//		Marker = OCTET
//	3. Marker 用来标记不同类型的 IdentifierComponent，目前分配如下
// 		Marker value		Description
//			0x00		普通的UTF-8编码的字符串
//			0x01		非负整数（可以用1、2、4、8个字节表示）
//			0x02		任意字节数组
//			0x03		版本号，非负整数（可以用1、2、4、8个字节表示）=> 专门用于内容标识中指定数据发布的版本号
//			0x04		分段号，非负整数（可以用1、2、4、8个字节表示）=> 专门用于内容标识中指定数据发布的分段号
//

const encoding = require('../encoding')
const {TlvComponentBase} = require('./TlvComponentBase')
const MarkerType = {
    MarkerString: 0x00,
    MarkerNonNegativeInteger: 0x01,
    MarkerByteArray: 0x02,
    MarkerVersionNumber: 0x03,
    MarkerFragmentNumber: 0x04
}
const utils = require('../utils')
const util = require('util');
////////////////////////////////////////
//// markermap 初始化
////////////////////////////////////////
let markerMap = {}
markerMap[MarkerType.MarkerString] = true
markerMap[MarkerType.MarkerNonNegativeInteger] = true
markerMap[MarkerType.MarkerByteArray] = true
markerMap[MarkerType.MarkerVersionNumber] = true
markerMap[MarkerType.MarkerFragmentNumber] = true

// const identifierComponent = require('./IdentifierComponent')
var aggregation = require('../common/aggregation')
const {sizeOfNonNegativeInteger} = require("../encoding/TLV");
const {buildNonNegativeIntegerArr} = require("../encoding/Encoder");
//实现了多继承
// var createSignatureInfoBySignatureTypeValue = require('./SignatureInfo').createSignatureInfoBySignatureTypeValue

class IdentifierComponent extends aggregation(TlvComponentBase,encoding.SelfEncodingBase,encoding.block.Block){
    constructor() {
        super()
        this.block = new encoding.block.Block()
        this.marker = 0 // byte // 标记 => 用来区分不同类型的 IdentifierComponent
        this.stringValue = '' // 如果 Marker == 0x00，则本组件存储一个字符串
        this.intValue = BigInt(0) // int64 // 如果 Marker == 0x01 || 0x03 || 0x04，则本组件存储一个非负整型
        this.byteArrayValue = Buffer.alloc(0) // []byte // 如果 Marker == 0x02，则本组件存储一个字节数组
    }

    /**
     * @description 以字符串形式获取本 IdentifierComponent 的值
     * @returns {string}
     */
    getString(){
        return this.stringValue
    }

    /**
     * @description 以非负整数的形式获取本 IdentifierComponent 的值
     * @returns {bigint}
     */
    getNonNegativeInteger(){
        return this.intValue
    }

    /**
     * @description 以字节数组的形式获取本 IdentifierComponent 的值
     * @returns {Buffer}
     */
    getByteArray(){
        return this.byteArrayValue
    }

    /**
     * @description 以版本号的形式获取本 IdentifierComponent 的值
     * @returns {bigint}
     */
    getVersionNumber(){
        return this.intValue
    }

    /**
     * @description 以分段号的形式获取本 IdentifierComponent 的值
     * @returns {bigint}
     */
    getFragmentNumber(){
        return this.intValue
    }

    /**
     * @description 判断当前 IdentifierComponent 是否是存储字符串的类型
     * @returns {boolean}
     */
    isString(){
        return this.marker === MarkerType.MarkerString
    }

    /**
     * @description 判断当前 IdentifierComponent 是否是存储非负整数的类型
     * @returns {boolean}
     */
    isNonNegativeInteger(){
        return this.marker === MarkerType.MarkerNonNegativeInteger
    }

    /**
     * @description 判断当前 IdentifierComponent 是否是存储字节数组的类型
     * @returns {boolean}
     */
    isByteArray(){
        return this.marker === MarkerType.MarkerByteArray
    }

    /**
     * @description 判断当前 IdentifierComponent 是否存储的是一个版本号语义的数字
     * @returns {boolean}
     */
    isVersionNumber(){
        return this.marker === MarkerType.MarkerVersionNumber
    }

    /**
     * @description 判断当前 IdentifierComponent 是否存储的是一个分段号语义的数字
     * @returns {boolean}
     */
    isFragmentNumber(){
        return this.marker === MarkerType.MarkerFragmentNumber
    }

    /**
     * @description 判断当前 IdentifierComponent 是否等于另一个 IdentifierComponent
     * @param target
     * @returns {boolean}
     */
    isSame(target){
        if(target === null){
            return false
        }
        if(this.marker === target.marker){
            if(this.isString()){
                return this.stringValue === target.stringValue
            }
            if((this.isNonNegativeInteger() && target.isNonNegativeInteger()) ||
                (this.isFragmentNumber() && target.isFragmentNumber()) ||
                (this.isVersionNumber() && target.isVersionNumber())){
                return this.intValue === target.intValue
            }
            if(this.isByteArray()){
                // Check whether a and b are the same length and contain the same bytes.
                let buf1 = Buffer.from(this.byteArrayValue)
                let buf2 = Buffer.from(target.getByteArray())
                if(this.byteArrayValue.byteLength === target.byteArrayValue.byteLength){
                    return buf1.toString('utf-8') === buf2.toString('utf-8')
                }
                return false
            }
        }
        return false
    }

    /**
     * @description 将 IdentifierComponent 使用百分号编码规则编码
     * @returns {string}
     */
    toUri(){
        let res = ''
        switch (this.marker){
            case MarkerType.MarkerString:
                res = utils.escape(this.stringValue)
                break
            case MarkerType.MarkerNonNegativeInteger:
                res = this.intValue.toString(10)
                break
            case MarkerType.MarkerByteArray:
                res = utils.escape(this.byteArrayValue.toString('utf-8'))
                break
        }
        return res
    }

    /**
     * @description 线速编码，将 IdentifierComponent 编码成网络中传输的字节数组格式
     * @param enc
     * @returns {*}
     */
    wireEncode(enc){
        try {
            var res = this.encode(enc)
            return res
        } catch (error) {
            throw error
        }
    }

    /**
     * @description 线速解码，将TLV Block解码成 IdentifierComponent
     * @param block
     */
    wireDecode(block){
        try {
            // 初始化block
            this.buildByBlock(block)
            this.block = block
        } catch (error) {
            throw error
        }

        let value = this.getValue()
        let length = this.getLength()
        if(length < 1){
            throw new IdentifierComponentError("Invalid length: "+length)
        }
        
        let view = new DataView(value)
        let ok = markerMap[view.getUint8(0)]
        this.marker = view.getUint8(0)

                switch (this.marker) {
                    case MarkerType.MarkerString:
                        let enc = new util.TextDecoder('utf-8')
                        let arr = new Uint8Array(value.slice(1, value.byteLength))
                        this.stringValue = enc.decode(arr)
                        break
                    case MarkerType.MarkerVersionNumber:
                        break
                    case MarkerType.MarkerFragmentNumber:
                        break
                    case MarkerType.MarkerNonNegativeInteger:
                        // 如果Marker指定是非负整型，则TLV-LENGTH的有效值为2、3、5、9
                        if (length === 2 || length === 3 || length === 5 || length === 9) {
                            this.intValue = encoding.tlv.readNonNegativeInteger(value.slice(1, value.byteLength), 0, length - 1)
                        }else{
                            throw new IdentifierComponentError(`MarkerNonNegativeInteger => Invalid value length: ${length}`)
                        }
                        break
                    case MarkerType.MarkerByteArray:
                        this.byteArrayValue = Buffer.from(value.slice(1, value.byteLength))
                        break
                }
    }

    /**
     * @description marker加上标识中的内容转换成的字符串数据
     * @returns {string}
     */
    toString(){
        let res = ''
        switch (this.marker){
            case MarkerType.MarkerString:
                res += this.marker + this.stringValue
                break
            case MarkerType.MarkerFragmentNumber:
                break
            case MarkerType.MarkerVersionNumber:
                break
            case MarkerType.MarkerNonNegativeInteger:
                res += this.marker + this.intValue.toString(10)
                break
            case MarkerType.MarkerByteArray:
                res += this.marker + this.byteArrayValue.toString('utf-8')
                break
        }
        return res
    }
}

//////////////////////////////////////////////////////////////////////////////////////////////
////// 构造函数 Create*
//////////////////////////////////////////////////////////////////////////////////////////////

/**
 * @description 根据一个字符串构造一个 IdentifierComponent
 * @param nameComponent
 * @returns {IdentifierComponent}
 */
function createIdentifierComponentByString(nameComponent) {
    let encoded = Buffer.from(nameComponent, 'utf-8')
    let identifierComponent = new IdentifierComponent()
    identifierComponent.setType(BigInt(encoding.tlv.ComponentType.TlvIdentifierComponent))
    identifierComponent.setLength(BigInt(encoded.byteLength + 1))
    let value = Buffer.alloc(Number(identifierComponent.getLength()))
    value[0] = MarkerType.MarkerString
    value.fill(encoded, 1, value.byteLength)
    identifierComponent.marker = MarkerType.MarkerString
    identifierComponent.setValue(value.buffer) // 对应golang中的嵌入类型
    identifierComponent.stringValue = nameComponent
    return identifierComponent
}

/**
 * @description 根据一个非负整数构造一个 IdentifierComponent
 * @param intValue
 * @returns {IdentifierComponent}
 */
function createIdentifierComponentByNonNegativeInteger(intValue){
    let identifierComponent = new IdentifierComponent()
    identifierComponent.setType(BigInt(encoding.tlv.ComponentType.TlvIdentifierComponent))
    identifierComponent.setLength(BigInt(encoding.tlv.sizeOfNonNegativeInteger(intValue) + 1))
    let value = Buffer.alloc(Number(identifierComponent.getLength()))
    //buffer.alloc函数的参数必须是number类型，可能会产生问题，比如当length过大转化为number时丢失精度等!!!
    value[0] = MarkerType.MarkerNonNegativeInteger
    identifierComponent.marker = MarkerType.MarkerNonNegativeInteger
    value.fill(encoding.tlv.buildNonNegativeIntegerArr(intValue), 1, value.byteLength)
    identifierComponent.setValue(value.buffer)
    identifierComponent.intValue = intValue
    return identifierComponent
}

/**
 * @description 根据一个字节数组构造一个 IdentifierComponent
 * @param buffer
 * @returns {IdentifierComponent}
 */
function createIdentifierComponentByByteArray(buffer){
    let identifierComponent = new IdentifierComponent()
    identifierComponent.setType(BigInt(encoding.tlv.ComponentType.TlvIdentifierComponent))
    identifierComponent.setLength(BigInt(buffer.byteLength + 1))
    let value = Buffer.alloc(Number(identifierComponent.getLength()))
    //buffer.alloc函数的参数必须为number类型,可能存在问题
    value[0] = MarkerType.MarkerByteArray
    identifierComponent.marker = MarkerType.MarkerByteArray
    value.fill(new Buffer(buffer), 1, value.byteLength)
    identifierComponent.setValue(value.buffer)
    identifierComponent.byteArrayValue = buffer
    return identifierComponent
}

/**
 * @description 根据一个 TLV Block 创建一个 IdentifierComponent
 * @param block
 * @returns {IdentifierComponent}
 */
function createIdentifierComponentByBlock(block){
    try{
        let identifierComponent = new IdentifierComponent()
        identifierComponent.wireDecode(block)
        return identifierComponent
    }catch (e){
        throw e
    }

}

/**
 * CreateVersionNumberIdentifierComponent 创建一个语义为版本号的 IdentifierComponent
 * @param versionNumber
 * @return {IdentifierComponent}
 */
function createVersionNumberIdentifierComponent(versionNumber){
    let identifierComponent = new IdentifierComponent()
    identifierComponent.setType(BigInt(encoding.tlv.ComponentType.TlvIdentifierComponent))
    identifierComponent.setLength(BigInt(sizeOfNonNegativeInteger(versionNumber)))
    let value = Buffer.alloc(Number(identifierComponent.getLength()))
    //buffer.alloc函数的参数必须为number类型,可能存在问题
    value[0] = MarkerType.MarkerVersionNumber
    identifierComponent.marker = MarkerType.MarkerVersionNumber
    let buffer = buildNonNegativeIntegerArr(versionNumber)
    value.fill(new Buffer(buffer), 1, value.byteLength)
    identifierComponent.setValue(value.buffer)
    identifierComponent.intValue = versionNumber
    return identifierComponent
}


/**
 * CreateFragmentNumberIdentifierComponent 创建一个语义为版本号的 IdentifierComponent
 * @param fragmentNumber
 * @return {IdentifierComponent}
 */
function createFragmentNumberIdentifierComponent(fragmentNumber){
    let identifierComponent = new IdentifierComponent()
    identifierComponent.setType(BigInt(encoding.tlv.ComponentType.TlvIdentifierComponent))
    identifierComponent.setLength(BigInt(sizeOfNonNegativeInteger(fragmentNumber)))
    let value = Buffer.alloc(Number(identifierComponent.getLength()))
    //buffer.alloc函数的参数必须为number类型,可能存在问题
    value[0] = MarkerType.MarkerVersionNumber
    identifierComponent.marker = MarkerType.MarkerVersionNumber
    let buffer = buildNonNegativeIntegerArr(fragmentNumber)
    value.fill(new Buffer(buffer), 1, value.byteLength)
    identifierComponent.setValue(value.buffer)
    identifierComponent.intValue = fragmentNumber
    return identifierComponent
}

class IdentifierComponentError extends Error{
    constructor(m){
        super(m)
        this.name = 'BlockError'
    }

    error(){
        console.log(`{this.name}: {this.message}`)
    }
}
module.exports = {
    IdentifierComponent: IdentifierComponent,
    createIdentifierComponentByBlock: createIdentifierComponentByBlock,
    createIdentifierComponentByByteArray: createIdentifierComponentByByteArray,
    createIdentifierComponentByNonNegativeInteger:createIdentifierComponentByNonNegativeInteger,
    createIdentifierComponentByString: createIdentifierComponentByString,
    createFragmentNumberIdentifierComponent: createFragmentNumberIdentifierComponent,
    createVersionNumberIdentifierComponent: createVersionNumberIdentifierComponent,
}
